package com.ec.bl.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.ec.bl.domain.vo.*;
import com.ec.bl.mapper.DashboardMapper;
import com.ec.bl.service.DashboardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
@Slf4j
@Service
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {

    private final DashboardMapper dashboardMapper;
    private final StringRedisTemplate stringRedisTemplate;

    // --- Redis Cache Configuration ---
    private static final String DASHBOARD_CACHE_PREFIX = "dashboard:";
    private static final String METRICS_KEY = DASHBOARD_CACHE_PREFIX + "metrics";
    private static final String APPOINTMENTS_KEY = DASHBOARD_CACHE_PREFIX + "tasks:appointments";
    private static final String APPLICATIONS_KEY = DASHBOARD_CACHE_PREFIX + "tasks:applications";
    private static final String FEEDBACK_KEY = DASHBOARD_CACHE_PREFIX + "tasks:feedback";
    private static final String ALERTS_KEY = DASHBOARD_CACHE_PREFIX + "alerts:health";
    private static final String TRENDS_CHART_KEY = DASHBOARD_CACHE_PREFIX + "charts:trends";
    // 缓存过期时间设置为 3 分钟，因为仪表盘数据更新频率不需要太高
    private static final long CACHE_EXPIRE_MINUTES = 3;

    @Override
    public DashboardMetricsVO getDashboardMetrics() {
        // 1. 查缓存
        String cachedResult = stringRedisTemplate.opsForValue().get(METRICS_KEY);
        if (StringUtils.hasText(cachedResult)) {
            try {
                return JSON.parseObject(cachedResult, DashboardMetricsVO.class);
            } catch (Exception e) {
                log.warn("Failed to parse dashboard metrics from cache. Key: {}", METRICS_KEY, e);
                stringRedisTemplate.delete(METRICS_KEY);
            }
        }

        // 2. 查数据库
        DashboardMetricsVO metrics = new DashboardMetricsVO();
        metrics.setResidentCount(dashboardMapper.countResidents());
        metrics.setHealthAlertsToday(dashboardMapper.countHealthAlertsToday());
        metrics.setVacantBeds(dashboardMapper.countVacantBeds());
        long pendingApps = dashboardMapper.getPendingApplications().size();
        long pendingAppointments = dashboardMapper.getPendingAppointments().size();
        long pendingFeedbacks = dashboardMapper.getLatestFeedback().size();
        metrics.setPendingTasks(pendingApps + pendingAppointments + pendingFeedbacks);

        // 3. 回写缓存
        stringRedisTemplate.opsForValue().set(METRICS_KEY, JSON.toJSONString(metrics), CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        return metrics;
    }

    @Override
    public List<PendingTaskVO> getPendingAppointments() {
        return getCachedList(APPOINTMENTS_KEY, new TypeReference<List<PendingTaskVO>>() {},
                () -> dashboardMapper.getPendingAppointments());
    }

    @Override
    public List<PendingTaskVO> getPendingApplications() {
        return getCachedList(APPLICATIONS_KEY, new TypeReference<List<PendingTaskVO>>() {},
                () -> dashboardMapper.getPendingApplications());
    }

    @Override
    public List<RecentFeedbackVO> getLatestFeedback() {
        return getCachedList(FEEDBACK_KEY, new TypeReference<List<RecentFeedbackVO>>() {},
                () -> dashboardMapper.getLatestFeedback());
    }

    @Override
    public List<HealthAlertVO> getRealtimeHealthAlerts() {
        return getCachedList(ALERTS_KEY, new TypeReference<List<HealthAlertVO>>() {},
                () -> dashboardMapper.getRealtimeHealthAlerts());
    }

    @Override
    public TrendsChartVO getCheckInTrends() {
        // 1. 查缓存
        String cachedResult = stringRedisTemplate.opsForValue().get(TRENDS_CHART_KEY);
        if (StringUtils.hasText(cachedResult)) {
            try {
                return JSON.parseObject(cachedResult, TrendsChartVO.class);
            } catch (Exception e) {
                log.warn("Failed to parse trends chart from cache. Key: {}", TRENDS_CHART_KEY, e);
                stringRedisTemplate.delete(TRENDS_CHART_KEY);
            }
        }

        // 2. 查数据库并计算
        TrendsChartVO chartVO = new TrendsChartVO();
        final int days = 30;
        LocalDate startDate = LocalDate.now().minusDays(days - 1);

        List<String> dateAxis = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd");
        for (int i = 0; i < days; i++) {
            dateAxis.add(startDate.plusDays(i).format(formatter));
        }
        chartVO.setDateAxis(dateAxis);

        List<Map<String, Object>> dailyCheckInsRaw = dashboardMapper.getNewCheckInsPerDay(startDate);
        Map<LocalDate, Long> dailyCheckInsMap = dailyCheckInsRaw.stream()
                .collect(Collectors.toMap(
                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
                        m -> (Long) m.get("count")
                ));

        List<Long> newCheckIns = new ArrayList<>();
        for (int i = 0; i < days; i++) {
            newCheckIns.add(dailyCheckInsMap.getOrDefault(startDate.plusDays(i), 0L));
        }
        chartVO.setNewCheckIns(newCheckIns);

        long totalBeds = dashboardMapper.countTotalBeds();
        if (totalBeds == 0) {
            chartVO.setBedOccupancyRates(LongStream.range(0, days).mapToObj(i -> 0.0).collect(Collectors.toList()));
        } else {
            long currentResidents = dashboardMapper.countResidents();
            long pastResidents = currentResidents - newCheckIns.stream().mapToLong(Long::longValue).sum();
            List<Double> occupancyRates = new ArrayList<>();
            long runningResidents = pastResidents;
            for (Long dailyNew : newCheckIns) {
                runningResidents += dailyNew;
                double rate = (double) runningResidents / totalBeds * 100.0;
                occupancyRates.add(Math.round(rate * 100.0) / 100.0);
            }
            chartVO.setBedOccupancyRates(occupancyRates);
        }

        // 3. 回写缓存
        stringRedisTemplate.opsForValue().set(TRENDS_CHART_KEY, JSON.toJSONString(chartVO), CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        return chartVO;
    }

    /**
     * 通用的列表缓存获取逻辑
     *
     * @param key       缓存key
     * @param typeRef   FastJSON的TypeReference，用于反序列化泛型列表
     * @param dbFallback 当缓存未命中时，用于查询数据库的函数
     * @return 列表数据
     */
    private <T> List<T> getCachedList(String key, TypeReference<List<T>> typeRef, java.util.function.Supplier<List<T>> dbFallback) {
        // 1. 查缓存
        String cachedResult = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.hasText(cachedResult)) {
            try {
                return JSON.parseObject(cachedResult, typeRef);
            } catch (Exception e) {
                log.warn("Failed to parse list from cache. Key: {}", key, e);
                stringRedisTemplate.delete(key);
            }
        }

        // 2. 查数据库
        List<T> dbResult = dbFallback.get();

        // 3. 回写缓存 (即使为空列表也缓存，防止缓存穿透)
        if (dbResult != null) {
            stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(dbResult), CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
        }

        return dbResult;
    }
}